use integer_encoding::VarInt;

/// Contains an offset and a length (or size); can be efficiently encoded in to varints. This is
/// used typically as file-internal pointer in table (SSTable) files. For example, the index block
/// in an SSTable is a block of (key = largest key in block) -> (value = encoded blockhandle of
/// block).4
///
/// 这段代码定义了一个名为 BlockHandle 的结构体，它的主要作用是作为文件内部的指针，用于定位和描述数据块（block）在文件中的位置和大小，
/// 常见见于类似 SSTable（Sorted String Table）这样的有序键值对存储结构中。
///
/// 典型使用场景：
///
/// 在 SSTable 等存储结构中，通常会有一个索引块（index block），其中存储着键值对映射：
///
/// 键（key）：某个数据块中最大的键
///
/// 值（value）：对应数据块的 BlockHandle（包含该数据块的偏移量和大小）
// 当需要查找某个键时，先在索引块中找到对应的 BlockHandle，再根据其 offset 和 size 直接从文件中读取目标数据块，避免了全文件扫描，大幅提高查询效率。
#[derive(Debug, Clone)]
pub struct BlockHandle {
    /// 表示数据块在文件中的起始偏移量（即从文件开头到数据块开始位置的字节数）。
    offset: usize,
    /// 表示数据块的大小（以字节为单位）。
    size: usize,
}

/// 这段代码是 BlockHandle 结构体的实现，提供了对 BlockHandle 的创建、编码、解码和访问功能，使其能够在内存表示和字节流之间进行转换，这对于文件存储和网络传输至关重要。
impl BlockHandle {
    /// Decodes a block handle from `from` and returns a block handle
    /// together with how many bytes were read from the slice.
    ///
    /// 功能：从字节切片（&[u8]）中解码出 BlockHandle 实例，并返回解码后的对象以及读取的字节数。
    ///
    /// 过程：
    ///
    /// 首先使用可变长度可变整数解码（usize::decode_var）从字节流中读取 offset（偏移量）及其占用的字节数 offsize。
    ///
    /// 接着从剩余的字节流（&from[offsize..]）中解码出 size（大小）及其占用的字节数 szsize。
    ///
    /// 将解码得到的 offset 和 size 封装成 BlockHandle，并返回它与总读取字节数（offsize + szsize）。
    ///
    /// 用途：从文件或网络中读取数据时，将序列化的字节流还原为可操作的 BlockHandle 对象。
    pub fn decode(from: &[u8]) -> Option<(BlockHandle, usize)> {
        let (off, offsize) = usize::decode_var(from)?;
        let (sz, szsize) = usize::decode_var(&from[offsize..])?;

        Some((
            BlockHandle {
                offset: off,
                size: sz,
            },
            offsize + szsize,
        ))
    }

    /// 功能：创建一个新的 BlockHandle 实例，是简单的构造函数。
    /// 用途：在需要手动创建 BlockHandle 时使用，直接指定 offset 和 size。
    pub fn new(offset: usize, size: usize) -> BlockHandle {
        BlockHandle { offset, size }
    }

    /// 功能：分别返回 BlockHandle 中存储的 offset（偏移量）。
    ///
    /// 用途：提供对结构体内部字段的只读访问，用于获取数据块的定位信息。
    pub fn offset(&self) -> usize {
        self.offset
    }

    /// 功能：分别返回 BlockHandle 中存储的 size（大小）值。
    ///
    /// 用途：提供对结构体内部字段的只读访问，用于获取数据块的定位信息。
    pub fn size(&self) -> usize {
        self.size
    }

    /// Returns how many bytes were written, or 0 if the write failed because `dst` is too small.
    ///
    /// 功能：将 BlockHandle 编码为字节流并写入目标缓冲区 dst，返回写入的字节数。
    ///
    /// 过程：
    ///
    /// 首先断言目标缓冲区有足够的空间（至少为 offset 和 size 编码所需空间之和）。
    ///
    /// 先将 offset 编码为可变长度整数并写入 dst，记录写入的字节数 off。
    ///
    /// 再从 dst 的 off 位置开始，将 size 编码为可变长度整数并写入，返回总写入字节数（off + 第二次写入的字节数）。
    ///
    /// 用途：在将 BlockHandle 存储到文件或通过网络发送时，将其序列化为字节流。
    pub fn encode_to(&self, dst: &mut [u8]) -> usize {
        assert!(dst.len() >= self.offset.required_space() + self.size.required_space());

        let off = self.offset.encode_var(dst);
        off + self.size.encode_var(&mut dst[off..])
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_blockhandle() {
        let bh = BlockHandle::new(890, 777);
        let mut dst = [0_u8; 128];
        let enc_sz = bh.encode_to(&mut dst[..]);

        let (bh2, dec_sz) = BlockHandle::decode(&dst).unwrap();

        assert_eq!(enc_sz, dec_sz);
        assert_eq!(bh.size(), bh2.size());
        assert_eq!(bh.offset(), bh2.offset());
    }
}
